package cn.tml.innermost.music.service.impl;

import cn.tml.innermost.framework.entity.enums.ResultCode;
import cn.tml.innermost.framework.exception.ServiceException;
import cn.tml.innermost.music.dos.*;
import cn.tml.innermost.music.dos.Label;
import cn.tml.innermost.music.params.LabelParams;
import cn.tml.innermost.music.vo.*;
import cn.tml.innermost.music.vo.LabelInfoVO;
import cn.tml.innermost.music.mapper.LabelMapper;
import cn.tml.innermost.music.mapper.LabelMusicMapper;
import cn.tml.innermost.music.mapper.LabelMusicListMapper;
import cn.tml.innermost.music.mapper.MusicListLabelMapper;
import cn.tml.innermost.music.service.LabelService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author 燧枫
 * @since 2022-10-18
 */
@Service
public class LabelServiceImpl extends ServiceImpl<LabelMapper, Label> implements LabelService {

    @Resource
    LabelMusicMapper labelMusicMapper;

    @Resource
    LabelMusicListMapper labelMusicListMapper;

    @Resource
    MusicListLabelMapper MusicListLabelMapper;

    @Autowired
    private LabelMapper labelMapper;

    @Autowired
    private RedisTemplate redisTemplate;

    // 歌单的redsi前缀
    private static final String MUSIC_LABEL = "music-label:";

    /***
     * 获取所有标签信息
     * @return: LabelAllVO
     */
    @Override
    public LabelAllVO getAllLabelInfo() {

        LabelAllVO labelAllVO = null;

        if (redisTemplate.hasKey(MUSIC_LABEL)) {
//        如果在redis中, 直接得到标签集合实体类
            labelAllVO = (LabelAllVO) redisTemplate.opsForValue().get(MUSIC_LABEL);
        } else {
            // 查询所有标签
            List<Label> labels = labelMapper.selectList(new QueryWrapper<>());

            // 按分类整理标签
            Map<String, List<LabelInfoVO>> labelMap = new HashMap<>();
            for (Label label : labels) {
                String category = label.getCategory();
                LabelInfoVO labelInfoVO = LabelInfoVO.valueOf(label);

                if (!labelMap.containsKey(category)) {
                    labelMap.put(category, new LinkedList<>());
                }
                labelMap.get(category).add(labelInfoVO);
            }

            // 转换为LabelAllVO对象
            labelAllVO = new LabelAllVO();
            List<CategoryList> categoryLists = new LinkedList<>();
            for (Map.Entry<String, List<LabelInfoVO>> entry : labelMap.entrySet()) {
                CategoryList categoryList = new CategoryList();
                categoryList.setCategoryName(entry.getKey());
                categoryList.setLabelInfoVOList(entry.getValue());
                categoryLists.add(categoryList);
            }
            labelAllVO.setCategoryLists(categoryLists);

//            保存至redis中
            redisTemplate.opsForValue().set(MUSIC_LABEL, labelAllVO, 60 * 60 * 24, TimeUnit.SECONDS);
        }

        return labelAllVO;
    }

    /***
     * 分页获取标签所有歌曲
     * @param labelId
     * @param 当前页码 current
     * @param 每页条数 size
     * @return: LabelMusicVO
     */
    @Override
    public LabelMusicVO getLabelAllMusic(Long labelId, Long current, Long size) {
//        限制一波分页大小（100）
        if (size > 100)
            throw new ServiceException(ResultCode.PAGE_TOO_BIG);
        LabelMusicVO labelMusicVO = null;
//        根据标签id得到标签实体类
        Label label = this.getById(labelId);
//        如果查不到，直接抛异常
        if (label == null)
            throw new ServiceException(ResultCode.LABEL_GET_FAIL);
//        如果删除标记为true，直接抛异常
        if (label.getDeleteFlag() == true)
            throw new ServiceException(ResultCode.LABEL_GET_FAIL);
//        从关系表中查出所有所有实体类
//        根据current与size直接查询
        Page<LabelMusic> page = new Page<>(current, size);
        labelMusicMapper.selectPage(page, new QueryWrapper<LabelMusic>().eq("label_id", labelId));
//        提取PageOV所需信息并封装
        PageOV pageOV = new PageOV();
        pageOV.setCurrent(current);
        pageOV.setSize(size);
        pageOV.setTotal(page.getTotal());
        pageOV.setPages(page.getPages());
        labelMusicVO = new LabelMusicVO(pageOV, LabelInfoVO.valueOf(label), page.getRecords());
        return labelMusicVO;
    }

    /***
     * 分页获取标签所有歌曲
     * @param labelId
     * @param 当前页码 current
     * @param 每页条数 size
     * @return: LabelMusicVO
     */
    @Override
    public LabelMusicListVO getLabelAllMusicList(Long labelId, Long current, Long size) {
//        限制一波分页大小（100）
        if (size > 100)
            throw new ServiceException(ResultCode.PAGE_TOO_BIG);
        LabelMusicListVO labelMusicListVO = null;
//        根据标签id得到标签实体类
        Label label = this.getById(labelId);
//        如果查不到，直接抛异常
        if (label == null)
            throw new ServiceException(ResultCode.LABEL_GET_FAIL);
//        如果删除标记为true，直接抛异常
        if (label.getDeleteFlag() == true)
            throw new ServiceException(ResultCode.LABEL_GET_FAIL);
//        从关系表中查出所有所有实体类
//        根据current与size直接查询
        Page<LabelMusicList> page = new Page<>(current, size);
        labelMusicListMapper.selectPage(page, new QueryWrapper<LabelMusicList>().eq("label_id", labelId));
//        提取PageOV所需信息并封装
        PageOV pageOV = new PageOV();
        pageOV.setCurrent(current);
        pageOV.setSize(size);
        pageOV.setTotal(page.getTotal());
        pageOV.setPages(page.getPages());
        labelMusicListVO = new LabelMusicListVO(pageOV, LabelInfoVO.valueOf(label), page.getRecords());
        return labelMusicListVO;
    }


    /***
     *  新建一个标签
     * @param labelParams
     */
    @Override
    public LabelInfoVO addLabel(LabelParams labelParams) {
//        先将labelParams转为实体类
        Label label = Label.valueOf(labelParams);
//        将实体类插入数据库中，如果失败，抛异常
        if (!this.save(label))
            throw new ServiceException(ResultCode.LABEL_SAVE_FAIL);
//        如果成功，初始化参数，转为LabelInfoVO并返回
        label.setName(labelParams.getName());
        label.setCoverUrl(labelParams.getCoverUrl());
        label.setDescription(labelParams.getDescription());
//        保存至redis中
        redisTemplate.opsForValue().set(MUSIC_LABEL + label.getId(), label, 60 * 60, TimeUnit.SECONDS);
        return LabelInfoVO.valueOf(label);
    }

    /**
     * @param labelId
     * @description: 删除一个标签
     */
    @Override
    public LabelInfoVO delLabel(Long labelId) {
//        根据标签id得到标签实体类
        Label label = this.getById(labelId);
//        如果查不到，直接抛异常
        if (label == null)
            throw new ServiceException(ResultCode.LABEL_GET_FAIL);
//        将删除标记至为true，并更新至数据库中，失败，抛异常
        label.setDeleteFlag(true);
        if (!updateById(label))
            throw new ServiceException(ResultCode.LABEL_DEL_FAIL);
//        将redsi中的album对象直接删除
        if (redisTemplate.hasKey(MUSIC_LABEL + labelId)) {
            if (!redisTemplate.delete(MUSIC_LABEL + labelId))
                throw new ServiceException(ResultCode.LABEL_DEL_FAIL);
        }
//        如果成功，转为MusicListInfoVO并返回
        return LabelInfoVO.valueOf(label);
    }

    /***
     * @description: 修改标签信息
     * @param labelId
     */
    @Override
    public LabelInfoVO updataLabel(Long labelId, LabelParams labelParams) {
//        根据标签id得到标签实体类
        Label label = this.getById(labelId);
//        如果查不到，直接抛异常
        if (label == null)
            throw new ServiceException(ResultCode.LABEL_GET_FAIL);
//        如果删除标记为true，直接抛异常
        if (label.getDeleteFlag() == true)
            throw new ServiceException(ResultCode.LABEL_GET_FAIL);
//        将实体类同步至labelParams，并更新至数据库中
        label.setId(labelId);
        label.setName(labelParams.getName());
        label.setCoverUrl(labelParams.getCoverUrl());
        label.setDescription(labelParams.getDescription());
        if (!updateById(label))
            throw new ServiceException(ResultCode.LABEL_UPDATE_FAIL);
//        查看是否在redis中
        if (redisTemplate.hasKey(MUSIC_LABEL + labelId)) {
//        如果在redis中, 先将将redsi中的album对象删除
            if (!redisTemplate.delete(MUSIC_LABEL + labelId))
                throw new ServiceException(ResultCode.LABEL_UPDATE_FAIL);
//        再将新的对象放进redis中
            redisTemplate.opsForValue().set(MUSIC_LABEL + labelId, label, 60 * 60, TimeUnit.SECONDS);
        }
//        如果成功，转为labelInfoVO并返回
        return LabelInfoVO.valueOf(label);
    }

    /***
     * @description: 添加歌曲到标签
     * @param musicLabelList
     */
    @Override
    public LabelInfoVO addMusicToLabel(List<LabelMusic> musicLabelList) {
//        根据标签id获取标签实体类
        Label label = this.getById(musicLabelList.get(0).getLabelId());
//        如果查不到，直接抛异常
        if (label == null)
            throw new ServiceException(ResultCode.LABEL_GET_FAIL);
//        直接将整个歌曲list添加至关系表中
        for (int i = 0; i < musicLabelList.size(); i++) {
            labelMusicMapper.insert(musicLabelList.get(i));
        }
//        再将此实体类更新至数据库中，失败，抛异常
        if (!updateById(label))
            throw new ServiceException(ResultCode.LABEL_UPDATE_FAIL);
//        如果成功，转为labelInfoVO并返回
        return LabelInfoVO.valueOf(label);
    }


    @Override
    public LabelInfoVO delMusicToLabel(List<LabelMusic> musicLabelList) {
//        根据标签id获取标签实体类
        Label label = this.getById(musicLabelList.get(0).getLabelId());
//        如果查不到，直接抛异常
        if (label == null)
            throw new ServiceException(ResultCode.LABEL_GET_FAIL);
        Long labelId = musicLabelList.get(0).getLabelId();
        for (int i = 0; i < musicLabelList.size(); i++) {
            Map<String, Object> columnMap = new HashMap<>();
            columnMap.put("label_id", labelId);
            columnMap.put("music_id", musicLabelList.get(i).getMusicId());
            labelMusicMapper.deleteByMap(columnMap);
        }
//        再将此实体类更新至数据库中，失败，抛异常
        if (!updateById(label))
            throw new ServiceException(ResultCode.LABEL_UPDATE_FAIL);
//        如果成功，转为LabelInfoVO并返回
        return LabelInfoVO.valueOf(label);
    }
}
